package com.example.netty.config;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Slf4j
@Component
//这个注解是为了本 handle 作为单例使用.方便统计一些信息，如连接数
//2.方便再所有channel值间共享以下而信息
//3.但是要注意线程同步的问题
@ChannelHandler.Sharable
//继承 SimpleChannelInboundHandle ，其中类型是 TextWebSocketFrame 是个文本帧，也有在其他样例中是直接用 String 接受，也是可以。
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 描述: 一旦连接建立，这会是第一个被执行的方法。
     *
     * @return void
     * @Param [ctx]
     **/
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerAdded 被执行，通道全局ID：{}", ctx.channel().id().asLongText());
        //添加到 channelGroup 通道组中
        NettyConfig.getChannelGroup().add(ctx.channel());
    }

    /**
     * 描述: 读取数据
     *
     * @return void
     * @Param [channelHandlerContext, textWebSocketFrame]
     **/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

        String channelMsg = msg.text();
        log.info("服务器收到消息啦：{}", channelMsg);

        JSONObject msgJson = JSONUtil.parseObj(channelMsg);
        String uId = msgJson.getStr("uid");
        String toUId = msgJson.getStr("toUId");
        String msgText = msgJson.getStr("msgText");
        NettyConfig.getUserChannelMap().put(uId, ctx.channel());
        //将用户 ID 作为自定义属性加入到 channel 中，方便随时 channel 中获取用户 ID
        AttributeKey<String> uIdKey = AttributeKey.valueOf("userId");
        ctx.channel().attr(uIdKey).setIfAbsent(uId);
        //
        if (!StringUtils.isEmpty(toUId)) {
            Channel channel = NettyConfig.getUserChannelMap().get(toUId);
            if (channel != null) {
                channel.writeAndFlush(new TextWebSocketFrame(msgText));
            } else {
                log.info("处理成离线消息,用户：{}，离线消息：{}", toUId, msgText);
            }

        }

    }

    /**
     * 描述: 删除通道
     *
     * @return void
     * @Param [ctx]
     **/
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerRemoved 被调用,通道ID:{}", ctx.channel().id().asLongText());
        NettyConfig.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
        ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("出现异常：", cause.getMessage());
        NettyConfig.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
        ctx.close();

    }

    /**
     * 描述: 删除用户ID 和通道的关系
     *
     * @return void
     * @Author Solming
     * @Date 15:28 2021/11/14
     * @Param [ctx]
     **/
    private void removeUserId(ChannelHandlerContext ctx) {
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        String userId = ctx.channel().attr(key).get();
        NettyConfig.getUserChannelMap().remove(userId);
    }

}
